#!/usr/bin/env python3
"""
气象预报服务 - 从Visual Crossing API获取今天和未来6天的气象数据
支持本地模拟数据生成，确保在网络不可用时也能正常工作
"""

import logging
import requests
import random
from datetime import datetime, date, timedelta
from typing import Dict, List, Optional, Any
import pandas as pd

from backend.config.database import get_db_session
from backend.config.weather_config import WeatherConfig
from backend.entities.weather_daily import WeatherDaily

logger = logging.getLogger(__name__)


class WeatherForecastService:
    """气象预报服务 - 使用Visual Crossing API"""
    
    def __init__(self):
        # Visual Crossing API配置
        self.base_url = "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline"
        
        # 从配置文件获取坐标
        self.latitude = WeatherConfig.LATITUDE
        self.longitude = WeatherConfig.LONGITUDE
        
        # 从配置文件获取API密钥
        self.api_key = WeatherConfig.get_api_key()
        
        # 气象字段配置 - Visual Crossing提供更完整的字段
        self.weather_fields = [
            "datetime", "tempmax", "tempmin", "temp", "feelslikemax", "feelslikemin", "feelslike",
            "dew", "humidity", "precip", "precipprob", "precipcover", "preciptype", "snow", "snowdepth",
            "windgust", "windspeed", "winddir", "pressure", "cloudcover", "visibility", "uvindex",
            "solarradiation", "solarenergy", "moonphase", "conditions", "description", "icon"
        ]
    
    def get_weather_forecast(self, days: int = 7) -> Optional[Dict[str, Any]]:
        """
        获取今天和未来几天的气象预报数据
        
        Args:
            days: 预报天数，默认7天（今天+未来6天）
            
        Returns:
            气象预报数据字典
        """
        try:
            # 计算日期范围
            start_date = date.today()
            end_date = start_date + timedelta(days=days-1)
            
            logger.info(f"获取Visual Crossing气象预报数据: {start_date} 到 {end_date}")
            
            # 构建API请求URL
            location = f"{self.latitude},{self.longitude}"
            url = f"{self.base_url}/{location}/{start_date}/{end_date}"
            
            # 检查API密钥是否配置
            if not self.api_key:
                logger.warning("Visual Crossing API密钥未配置，使用模拟数据")
                return self._generate_mock_weather_forecast(days)
            
            # 构建请求参数
            params = {
                "unitGroup": "metric",  # 使用公制单位
                "include": "days,hours",  # 包含每日和每小时数据
                "key": self.api_key,
                "contentType": "json"
            }
            
            # 发送API请求
            response = requests.get(url, params=params, timeout=WeatherConfig.REQUEST_TIMEOUT)
            
            if response.status_code == 200:
                data = response.json()
                logger.info(f"成功获取Visual Crossing气象预报数据")
                return data
            else:
                logger.error(f"Visual Crossing API请求失败: {response.status_code} - {response.text}")
                return None
                
        except Exception as e:
            logger.error(f"获取Visual Crossing气象预报数据失败: {e}")
            logger.info("使用本地模拟数据")
            return self._generate_mock_weather_forecast(days)
    
    def _generate_mock_weather_forecast(self, days: int = 7) -> Optional[Dict[str, Any]]:
        """
        生成模拟的气象预报数据
        
        Args:
            days: 预报天数
            
        Returns:
            模拟的气象数据字典
        """
        try:
            start_date = date.today()
            end_date = start_date + timedelta(days=days-1)
            
            # 生成日期列表
            date_list = []
            for i in range(days):
                current_date = start_date + timedelta(days=i)
                date_list.append(current_date.isoformat())
            
            # 生成模拟的每日数据
            daily_data = {
                "datetime": date_list,
                "tempmax": [],
                "tempmin": [],
                "temp": [],
                "feelslikemax": [],
                "feelslikemin": [],
                "feelslike": [],
                "dew": [],
                "humidity": [],
                "precip": [],
                "precipprob": [],
                "precipcover": [],
                "preciptype": [],
                "snow": [],
                "snowdepth": [],
                "windgust": [],
                "windspeed": [],
                "winddir": [],
                "pressure": [],
                "cloudcover": [],
                "visibility": [],
                "uvindex": [],
                "solarradiation": [],
                "solarenergy": [],
                "moonphase": [],
                "conditions": [],
                "description": [],
                "icon": []
            }
            
            # 为每天生成模拟数据
            for i in range(days):
                # 基础温度（夏季模式）
                base_temp = 20 + random.uniform(-5, 10)
                t_max = round(base_temp + random.uniform(5, 10), 1)
                t_min = round(base_temp - random.uniform(5, 10), 1)
                t_avg = round((t_max + t_min) / 2, 1)
                
                daily_data["tempmax"].append(t_max)
                daily_data["tempmin"].append(t_min)
                daily_data["temp"].append(t_avg)
                daily_data["feelslikemax"].append(round(t_max + random.uniform(-2, 2), 1))
                daily_data["feelslikemin"].append(round(t_min + random.uniform(-2, 2), 1))
                daily_data["feelslike"].append(round(t_avg + random.uniform(-2, 2), 1))
                
                # 湿度数据
                humidity = round(random.uniform(40, 90), 1)
                dew = round(t_avg - random.uniform(5, 15), 1)
                daily_data["humidity"].append(humidity)
                daily_data["dew"].append(dew)
                
                # 降水数据
                precip_chance = random.random()
                if precip_chance < 0.3:  # 30%概率下雨
                    precip = round(random.uniform(0.1, 15.0), 1)
                    precip_prob = round(random.uniform(60, 100), 1)
                    precip_cover = round(random.uniform(0.1, 1.0), 2)
                    preciptype = ["rain"] if random.random() > 0.1 else ["snow"]
                    snow = round(random.uniform(0, 2.0), 1) if "snow" in preciptype else 0.0
                    snowdepth = round(random.uniform(0, 5.0), 1) if snow > 0 else 0.0
                else:
                    precip = 0.0
                    precip_prob = round(random.uniform(0, 30), 1)
                    precip_cover = 0.0
                    preciptype = []
                    snow = 0.0
                    snowdepth = 0.0
                
                daily_data["precip"].append(precip)
                daily_data["precipprob"].append(precip_prob)
                daily_data["precipcover"].append(precip_cover)
                daily_data["preciptype"].append(preciptype)
                daily_data["snow"].append(snow)
                daily_data["snowdepth"].append(snowdepth)
                
                # 风速和风向
                windgust = round(random.uniform(10, 35), 1)
                windspeed = round(random.uniform(5, 25), 1)
                winddir = random.randint(0, 360)
                daily_data["windgust"].append(windgust)
                daily_data["windspeed"].append(windspeed)
                daily_data["winddir"].append(winddir)
                
                # 其他气象数据
                pressure = round(random.uniform(1000, 1020), 1)
                cloudcover = round(random.uniform(0, 100), 1)
                visibility = round(random.uniform(8, 16), 1)
                uvindex = round(random.uniform(1, 8), 1)
                solarradiation = round(random.uniform(5, 25), 1)
                solarenergy = round(random.uniform(10, 50), 1)
                moonphase = round(random.uniform(0, 1), 2)
                
                daily_data["pressure"].append(pressure)
                daily_data["cloudcover"].append(cloudcover)
                daily_data["visibility"].append(visibility)
                daily_data["uvindex"].append(uvindex)
                daily_data["solarradiation"].append(solarradiation)
                daily_data["solarenergy"].append(solarenergy)
                daily_data["moonphase"].append(moonphase)
                
                # 天气状况描述
                conditions = "Clear" if cloudcover < 30 else "Partly Cloudy" if cloudcover < 70 else "Cloudy"
                if precip > 0:
                    conditions = "Rain" if "rain" in preciptype else "Snow"
                
                daily_data["conditions"].append(conditions)
                daily_data["description"].append(f"{conditions} with {humidity}% humidity")
                daily_data["icon"].append("clear-day" if conditions == "Clear" else "partly-cloudy-day")
            
            logger.info(f"生成 {days} 天模拟Visual Crossing气象预报数据")
            # 转换为列表格式以匹配Visual Crossing API返回格式
            days_list = []
            for i in range(days):
                day_data = {
                    'datetime': date_list[i],
                    'tempmax': daily_data["tempmax"][i],
                    'tempmin': daily_data["tempmin"][i],
                    'temp': daily_data["temp"][i],
                    'feelslikemax': daily_data["feelslikemax"][i],
                    'feelslikemin': daily_data["feelslikemin"][i],
                    'feelslike': daily_data["feelslike"][i],
                    'dew': daily_data["dew"][i],
                    'humidity': daily_data["humidity"][i],
                    'precip': daily_data["precip"][i],
                    'precipprob': daily_data["precipprob"][i],
                    'precipcover': daily_data["precipcover"][i],
                    'preciptype': daily_data["preciptype"][i],
                    'snow': daily_data["snow"][i],
                    'snowdepth': daily_data["snowdepth"][i],
                    'windgust': daily_data["windgust"][i],
                    'windspeed': daily_data["windspeed"][i],
                    'winddir': daily_data["winddir"][i],
                    'pressure': daily_data["pressure"][i],
                    'cloudcover': daily_data["cloudcover"][i],
                    'visibility': daily_data["visibility"][i],
                    'uvindex': daily_data["uvindex"][i],
                    'solarradiation': daily_data["solarradiation"][i],
                    'solarenergy': daily_data["solarenergy"][i],
                    'moonphase': daily_data["moonphase"][i],
                    'conditions': daily_data["conditions"][i],
                    'description': daily_data["description"][i],
                    'icon': daily_data["icon"][i]
                }
                days_list.append(day_data)
            
            return {"days": days_list}
            
        except Exception as e:
            logger.error(f"生成模拟Visual Crossing气象数据失败: {e}")
            return None
    
    def save_weather_forecast_to_db(self, forecast_data: Dict[str, Any]) -> bool:
        """
        仅保存历史和昨天的气象数据到数据库，不保存今天及未来的数据
        """
        try:
            from datetime import date, timedelta
            today = date.today()
            with get_db_session() as db:
                days_data = forecast_data.get('days', [])
                if not days_data:
                    logger.warning("没有找到Visual Crossing气象数据")
                    return False
                saved_count = 0
                for day_data in days_data:
                    try:
                        date_str = day_data.get('datetime', '')
                        if not date_str:
                            continue
                        forecast_date = datetime.strptime(date_str, '%Y-%m-%d').date()
                        # 只保存昨天及更早的数据
                        if forecast_date >= today:
                            continue
                        existing_record = db.query(WeatherDaily).filter(
                            WeatherDaily.dt == forecast_date
                        ).first()
                        if existing_record:
                            logger.info(f"日期 {date_str} 的气象数据已存在，跳过")
                            continue
                        weather_record = WeatherDaily(
                            dt=forecast_date,
                            t_max=day_data.get('tempmax'),
                            t_min=day_data.get('tempmin'),
                            humidity=day_data.get('humidity'),
                            precip=day_data.get('precip'),
                            snow=day_data.get('snow'),
                            wind_dir=day_data.get('winddir'),
                            ws_max=day_data.get('windspeed'),
                            pressure=day_data.get('pressure'),
                            uv_index=day_data.get('uvindex'),
                            data_source='visualcrossing'
                        )
                        db.add(weather_record)
                        saved_count += 1
                    except Exception as e:
                        logger.error(f"保存日期 {date_str} 的Visual Crossing气象数据失败: {e}")
                        continue
                db.commit()
                logger.info(f"成功保存 {saved_count} 条Visual Crossing气象预报数据到数据库")
                return True
        except Exception as e:
            logger.error(f"保存Visual Crossing气象预报数据到数据库失败: {e}")
            return False

    def get_weather_forecast_for_prediction(self, target_date: date) -> Optional[Dict[str, Any]]:
        """
        预测时：
        - 历史和昨天：查数据库
        - 今天及未来：直接API获取，不查库
        """
        try:
            from datetime import date as dt_date
            today = dt_date.today()
            if target_date >= today:
                # 今天及未来，直接API获取
                logger.info(f"直接通过API获取 {target_date} 的Visual Crossing气象数据（不查库）")
                forecast_data = self.get_weather_forecast((target_date - today).days + 1)
                if forecast_data:
                    days_data = forecast_data.get('days', [])
                    for day in days_data:
                        if day.get('datetime') == target_date.isoformat():
                            return {
                                't_max': day.get('tempmax'),
                                't_min': day.get('tempmin'),
                                'humidity': day.get('humidity'),
                                'precip': day.get('precip'),
                                'snow': day.get('snow'),
                                'wind_dir': day.get('winddir'),
                                'pressure': day.get('pressure'),
                                'wind_speed': day.get('windspeed'),
                                'uv_index': day.get('uvindex')
                            }
                return None
            # 历史和昨天，查数据库
            with get_db_session() as db:
                weather_record = db.query(WeatherDaily).filter(
                    WeatherDaily.dt == target_date
                ).first()
                if weather_record:
                    return {
                        't_max': weather_record.t_max,
                        't_min': weather_record.t_min,
                        'humidity': weather_record.humidity,
                        'precip': weather_record.precip,
                        'snow': weather_record.snow,
                        'wind_dir': weather_record.wind_dir,
                        'pressure': weather_record.pressure,
                        'wind_speed': weather_record.ws_max,
                        'uv_index': weather_record.uv_index
                    }
            return None
        except Exception as e:
            logger.error(f"获取Visual Crossing预测用气象数据失败: {e}")
            return None
    
    def update_weather_forecast(self) -> bool:
        """
        更新Visual Crossing气象预报数据（获取今天和未来6天的数据）
        
        Returns:
            更新是否成功
        """
        try:
            logger.info("开始更新Visual Crossing气象预报数据...")
            
            # 获取7天预报数据
            forecast_data = self.get_weather_forecast(7)
            
            if forecast_data:
                # 保存到数据库
                success = self.save_weather_forecast_to_db(forecast_data)
                if success:
                    logger.info("Visual Crossing气象预报数据更新成功")
                    return True
                else:
                    logger.error("保存Visual Crossing气象预报数据失败")
                    return False
            else:
                logger.error("获取Visual Crossing气象预报数据失败")
                return False
                
        except Exception as e:
            logger.error(f"更新Visual Crossing气象预报数据失败: {e}")
            return False


# 创建服务实例
weather_forecast_service = WeatherForecastService() 